﻿#region
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Linq;
using System.Data.SqlClient;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Text;

#endregion

namespace DevTools.Common.Components
{
    public static class CommonUtility
    {
        public const string Delimiter = "#";

        /// <summary>
        ///     #1##2##3#  -->  List<int />{1,2,3}
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static List<int> SplitToIntList(string text)
        {
            if (String.IsNullOrEmpty(text))
                return new List<int>();

            var tokens = text.Split(new[] { Delimiter + Delimiter }, StringSplitOptions.RemoveEmptyEntries);

            tokens[0] = tokens[0].Replace(Delimiter, String.Empty);
            tokens[tokens.Length - 1] = tokens[tokens.Length - 1].Replace(Delimiter, String.Empty);

            return tokens.Select(Int32.Parse).ToList();
        }

        /// <summary>
        ///     List<int />{1,2,3}  -->  #1##2##3#
        /// </summary>
        /// <param name="intList"></param>
        /// <returns></returns>
        public static string JoinToCodeString(List<int> intList)
        {
            if (intList.Count == 0)
                return String.Empty;

            string[] tokens = intList.Select(x => x.ToString()).ToArray();

            return Delimiter + String.Join(Delimiter + Delimiter, tokens) + Delimiter;
        }

        /// <summary>
        ///     List<int />{1,2,3}  -->  코드1, 코드2, 코드3
        /// </summary>
        /// <param name="intList"></param>
        /// <returns></returns>
        public static string JoinToTextString(List<int> intList)
        {
            return JoinToTextString(intList, null);
        }

        /// <summary>
        ///     List<int />{1,2,3}  -->  코드1, 코드2, 코드3
        /// </summary>
        /// <param name="intList"></param>
        /// <param name="convertFunc">int -> string 변환 메서드. null 이면 LocalData.GetCodeText 을 사용</param>
        /// <returns></returns>
        public static string JoinToTextString(List<int> intList, Func<int, string> convertFunc)
        {
            //            if (intList.Count == 0)
            //                return string.Empty;
            //
            //            string[] tokens = null;
            //            if (convertFunc == null)
            //                tokens = intList.Select(x => LocalData.GetCodeText(x)).ToArray();
            //            else
            //                tokens = intList.Select(convertFunc).ToArray();
            //
            //            return string.Join(", ", tokens);

            return null;
        }

        /// <summary>
        ///     #1##2##3#  -->  코드1, 코드2, 코드3
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string ConvertToCodeText(string text)
        {
            return ConvertToCodeText(text, null);
        }

        /// <summary>
        ///     #1##2##3#  -->  코드1, 코드2, 코드3
        /// </summary>
        /// <param name="text"></param>
        /// <param name="convertFunc">int -> string 변환 메서드. null 이면 LocalData.GetCodeText 을 사용</param>
        /// <returns></returns>
        public static string ConvertToCodeText(string text, Func<int, string> convertFunc)
        {
            if (String.IsNullOrEmpty(text))
                return String.Empty;

            return JoinToTextString(SplitToIntList(text), convertFunc);
        }

        public static string AttachDelimiters(int id)
        {
            return Delimiter + id + Delimiter;
        }

        /// <summary>
        ///     컬렉션의 각 아이템을 구분문자로 구분된 한 줄 짜리 문자열로 변환한다. 구분자로 ";"를 사용한다.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <returns></returns>
        public static string ToLine<T>(this IEnumerable<T> list)
        {
            return ToLine(list, ";");
        }

        /// <summary>
        ///     컬렉션의 각 아이템을 구분문자로 구분된 한 줄 짜리 문자열로 변환한다.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <param name="seperator">구분 문자</param>
        /// <returns></returns>
        public static string ToLine<T>(this IEnumerable<T> list, string seperator)
        {
            var tokens = list.Select(x => x.ToString()).ToArray();
            return String.Join(seperator, tokens);
        }

        /// <summary>
        ///     구분문자로 쪼개어 리스트를 만든다.
        /// </summary>
        /// <param name="line"></param>
        /// <returns></returns>
        public static List<int> ToIntList(this string line)
        {
            var tokens = line.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
            var list = tokens.Select(Int32.Parse);
            return list.ToList();
        }

        /// <summary>
        ///     리플렉션을 사용하여 객체를 복사한다.
        /// </summary>
        /// <typeparam name="T">기본 생성자를 지원하여야 한다.</typeparam>
        /// <param name="source">원본. null 이면 예외가 발생한다.</param>
        /// <returns>복제된 객체</returns>
        public static T Clone<T>(this T source) where T : new()
        {
            T clone = new T();

            Copy(source, clone);

            return clone;
        }

        /// <summary>
        ///     리플렉션을 사용하여 객체를 복사한다.
        /// </summary>
        /// <param name="source">원본. null 이면 예외가 발생한다.</param>
        /// <param name="target">대상 </param>
        /// <returns>복제된 객체</returns>
        public static void Copy<T>(T source, T target)
        {
            CopyAsObject(source, target);
        }

        /// <summary>
        ///     리플렉션을 사용하여 객체를 복사한다.
        /// </summary>
        /// <param name="source">원본. null 이면 예외가 발생한다.</param>
        /// <param name="target">대상 </param>
        /// <returns>복제된 객체</returns>
        public static void CopyAsObject(object source, object target)
        {
            foreach (PropertyInfo propertyInfo in source.GetType().GetProperties())
                if (propertyInfo.CanRead && propertyInfo.CanWrite)
                    propertyInfo.SetValue(target, propertyInfo.GetValue(source, null), null);
        }

        #region compress / decompress
        public static byte[] Compress(byte[] bytes)
        {
            MemoryStream ms = new MemoryStream();
            DeflateStream ds = new DeflateStream(ms, CompressionMode.Compress);

            ds.Write(bytes, 0, bytes.Length);
            ds.Close();

            return ms.ToArray();
        }

        public static byte[] Decompress(byte[] bytes)
        {
            MemoryStream ms = new MemoryStream(bytes);
            DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress);

            byte[] buffer = new byte[bytes.Length * 20];
            int count = ds.Read(buffer, 0, buffer.Length);
            ds.Close();

            byte[] result = new byte[count];
            Array.Copy(buffer, result, count);
            return result;
        }
        #endregion

        /// <summary>
        ///     우편번호를 포함하여 주소 텍스트를 조립한다.
        /// </summary>
        /// <param name="zipCode"></param>
        /// <param name="address1"></param>
        /// <param name="address2"></param>
        /// <returns></returns>
        public static string BuildAddress(string zipCode, string address1, string address2)
        {
            if (String.IsNullOrEmpty(zipCode))
                return String.Format("{0} {1}", address1, address2);
            else
                return String.Format("{0}{1} {2} {3}", "우.", zipCode, address1, address2);
        }

        /// <summary>
        ///     우편번호 없는 주소를 두 줄로 조립한다.
        /// </summary>
        /// <param name="address1"></param>
        /// <param name="address2"></param>
        /// <returns></returns>
        public static string BuildAddressWithoutZipCode(string address1, string address2)
        {
            return String.Format("{0}{1}{2}", address1, Environment.NewLine, address2);
        }

        /// <summary>
        ///     yyyy-MM-dd HH:mm:ss
        /// </summary>
        /// <param name="dateTime"></param>
        /// <returns></returns>
        public static string ToDateTimeText(this DateTime dateTime)
        {
            return dateTime.ToString("yyyy-MM-dd HH:mm:ss");
        }

        /// <summary>
        ///     yyyy-MM-dd HH:mm:ss
        /// </summary>
        /// <param name="dateTime"></param>
        /// <returns></returns>
        public static string ToDateTimeText(this DateTime? dateTime)
        {
            return dateTime == null ? string.Empty : ToDateTimeText(dateTime.Value);
        }

        /// <summary>
        ///     yyyy-MM-dd
        /// </summary>
        /// <param name="dateTime"></param>
        /// <returns></returns>
        public static string ToDateText(this DateTime dateTime)
        {
            return dateTime.ToString("yyyy-MM-dd");
        }

        /// <summary>
        ///     yyyy-MM-dd
        /// </summary>
        /// <param name="dateTime"></param>
        /// <returns></returns>
        public static string ToDateText(this DateTime? dateTime)
        {
            return dateTime == null ? string.Empty : ToDateText(dateTime.Value);
        }

        /// <summary>
        ///     yyyyMMdd
        /// </summary>
        /// <param name="dateTime"></param>
        /// <returns></returns>
        public static string To8DigitDateText(this DateTime dateTime)
        {
            return dateTime.ToString("yyyyMMdd");
        }

        /// <summary>
        ///     yyyyMMdd
        /// </summary>
        /// <param name="dateTime"></param>
        /// <returns></returns>
        public static string To8DigitDateText(this DateTime? dateTime)
        {
            return dateTime == null ? string.Empty : To8DigitDateText(dateTime.Value);
        }

        /// <summary>
        ///     HH:mm:ss
        /// </summary>
        /// <param name="dateTime"></param>
        /// <returns></returns>
        public static string ToTimeText(this DateTime dateTime)
        {
            return dateTime.ToString("HH:mm:ss");
        }

        /// <summary>
        ///     HH:mm:ss
        /// </summary>
        /// <param name="dateTime"></param>
        /// <returns></returns>
        public static string ToTimeText(this DateTime? dateTime)
        {
            return dateTime == null ? string.Empty : ToTimeText(dateTime.Value);
        }

        public static readonly DateTime SqlMinDate = new DateTime(1900, 1, 1);
        public static readonly DateTime SqlMaxDate = new DateTime(2079, 6, 5);

        public static object GetPropertyValue(object entity, Type type, string propertyName)
        {
            var property = type.GetProperty(propertyName);

            if (property == null)
                return null;

            return property.GetValue(entity, null);
        }

        public static object GetPropertyValue(object entity, string propertyName)
        {
            return GetPropertyValue(entity, entity.GetType(), propertyName);
        }

        public static void SetPropertyValue(object entity, Type type, string propertyName, object newValue)
        {
            var property = type.GetProperty(propertyName);

            if (property == null)
                return;

            property.SetValue(entity, newValue, null);
        }

        public static void SetPropertyValue(object entity, string propertyName, object newValue)
        {
            SetPropertyValue(entity, entity.GetType(), propertyName, newValue);
        }

        public static bool CanConectToDB(string connectionString)
        {
            SqlConnection connection = new SqlConnection(connectionString);

            try
            {
                connection.Open();
                return true;
            }
            catch
            {
                return false;
            }
            finally
            {
                if (connection.State == ConnectionState.Open)
                    connection.Close();

                connection.Dispose();
            }
        }

        public static string ToCompactString(this DateTime dateTime)
        {
            return dateTime.ToString("yyyyMMdd");
        }

        public static string RemoveInvalidCharactersForCodeGeneration(string text)
        {
            char[] charactersToRemove = new[] { '(', ')', '-', ' ', ',', '&', '.', '/', '`' };

            StringBuilder builder = new StringBuilder();

            foreach (var c in text)
            {
                if (charactersToRemove.Contains(c))
                    continue;

                builder.Append(c);
            }

            return builder.ToString();
        }

        public static DateTime GetEndOfTheDay(this DateTime dateTime)
        {
            return dateTime.Date.Add(new TimeSpan(23, 59, 59));
        }

        public static DateTime GetFirstDayOfNextMonth(this DateTime date)
        {
            date = date.AddMonths(1);
            return new DateTime(date.Year, date.Month, 1);
        }

        public static DateTime GetLastDayOfTheMonth(this DateTime date)
        {
            int day = DateTime.DaysInMonth(date.Year, date.Month);
            return new DateTime(date.Year, date.Month, day);
        }

        #region ftp job
        public static string GetLocalPath(int entityID, string fileName)
        {
            //            var folder = LocalStorage.StorageDirectory;
            var folder = "";
            fileName = String.Format("{0}{1}{2}", entityID, DateTime.Now.Ticks, Path.GetExtension(fileName));

            return Path.Combine(folder, fileName);
        }
        #endregion

        #region 문서 수집기 잡
        /// <summary>
        ///     하드 디스크의 루트 디렉토리를 반환한다.
        /// </summary>
        /// <returns></returns>
        public static List<DirectoryInfo> GetHardDiskRoots()
        {
            List<DirectoryInfo> list = new List<DirectoryInfo>();

            var drives = Environment.GetLogicalDrives();
            foreach (var drive in drives)
            {
                DriveInfo driveInfo = new DriveInfo(drive);
                if (driveInfo.DriveType == DriveType.Fixed)
                    list.Add(driveInfo.RootDirectory);
            }

            return list;
        }
        #endregion

        public static string GetTextForMultiplePersons(string firstOneName, int count)
        {
            switch (count)
            {
                case 0:
                    return String.Empty;
                case 1:
                    return firstOneName ?? String.Empty;
                default:
                    return String.Format("{0} 외 {1}명", firstOneName, count - 1);
            }
        }

        public static string GetPersonInfo(string name, string phone, string fax)
        {
            return String.Format("{0} T.{1} F.{2}", name, phone, fax);
        }

        /// <summary>
        ///     string 리소스를 반환한다.
        /// </summary>
        /// <param name="resourceType">리포스 파일 타입. ex) typeof(Tiss.Resources)</param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string TranslateStringInResource(Type resourceType, string key)
        {
            var property = resourceType.GetProperty(key);

            if (property == null)
                return key;
            else
                return (string)property.GetValue(null, null);
        }

        public static void Merge<T, IDType>(this List<T> sourceList, List<T> targetList, Func<T, IDType> getIDFunc)
            where T : class
        {
            foreach (var target in targetList)
            {
                T source = sourceList.Find(x => getIDFunc(x).Equals(getIDFunc(target)));
                if (source == null)
                    sourceList.Add(target);
            }
        }

        /// <summary>
        ///     리스트를 2000 개씩으로 분할한다.
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        public static List<List<int>> SplitBy2000(List<int> list)
        {
            List<List<int>> returnList = new List<List<int>>();

            if (list == null || list.Count == 0)
                return returnList;

            int i = 0;
            const int MaxCount = 2000;

            do
            {
                int count = Math.Min(MaxCount, list.Count - i);
                int[] subList = new int[count];
                list.CopyTo(i, subList, 0, count);
                returnList.Add(subList.ToList());
                i += count;
            } while (i < list.Count);

            return returnList;
        }

        /// <summary>
        ///     예외 내용이 포함된 예외가 발생했다는 메시지를 반환한다.
        /// </summary>
        /// <param name="exception"></param>
        /// <returns></returns>
        public static string GetErrorIncludedExceptionMessage(string exception)
        {
            return "예외가 발생하였습니다." + Environment.NewLine + Environment.NewLine + exception;
        }

        /// <summary>
        ///     구분자(;)가 포함된 문자열을 리스트로 분리한다.
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static List<string> SplitToStringList(string text)
        {
            return text.Split(new[] { ';' }, StringSplitOptions.None).ToList();
        }

        /// <summary>
        ///     리스트의 각 항목을 하나의 문자열로 합친다.
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        public static string MergeToString(List<string> list)
        {
            return String.Join(";", list.ToArray());
        }

        public static Image ToImage(this Binary binary)
        {
            return binary.ToArray().ToImage();
        }

        private static ImageConverter _imageConverter;

        private static ImageConverter ImageConverter
        {
            get
            {
                if (_imageConverter == null)
                    _imageConverter = new ImageConverter();

                return _imageConverter;
            }
        }

        public static Image ToImage(this byte[] bytes)
        {
            return (Image) ImageConverter.ConvertFrom(bytes);
        }

        public static byte[] ToBytes(this Image image)
        {
            return (byte[]) ImageConverter.ConvertTo(image, typeof(byte[]));
        }

        /// <summary>
        ///     파일의 크기를 반환한다. 단위는 바이트.
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public static int GetFileSize(string filePath)
        {
            FileInfo fileInfo = new FileInfo(filePath);
            return (int)fileInfo.Length;
        }

        public static string BuildEmailLink(string subject, params string[] emails)
        {
            return String.Format(@"mailto:{0}?subject={1}", String.Join("; ", emails), subject);
        }

        /// <summary>
        ///     크기를 바이트 단위로 변환한다.
        /// </summary>
        /// <param name="size"></param>
        /// <returns></returns>
        public static string ConvertSizeToByteUnit(long size)
        {
            string[] units = new[] { "B", "K", "MB", "GB" };

            int i = 0;

            while (size > 1024)
            {
                size = size / 1024;
                i++;
            }

            return String.Format("{0} {1}", size, units[i]);
        }

        /// <summary>
        ///     리스트에서 처음 N개를 구한다. 리스트의 count가 N 보다 적으면 둘 중 적은 갯수를 반환한다.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        public static List<T> FirstN<T>(this List<T> list, int n)
        {
            int count = Math.Min(list.Count, n);

            return list.Take(count).ToList();
        }

        public static Stream ToStream(this Image image)
        {
            var stream = new MemoryStream();
            image.Save(stream, ImageFormat.Png);
            stream.Position = 0;
            return stream;
        }

        public static Stream ToStream(this Image image, ImageFormat formaw)
        {
            var stream = new MemoryStream();
            image.Save(stream, formaw);
            stream.Position = 0;
            return stream;
        }

        #region DataTable
        /// <summary>
        /// DataTable을 직렬화한다. 스키마와 직렬화된 XML을 각각 원소로 하는 배열을 반환한다.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <returns></returns>
        public static string[] SerializeDataTable(DataTable dataTable)
        {
            string[] values = new string[2];

            using (StringWriter writer = new StringWriter())
            {
                dataTable.WriteXmlSchema(writer);
                values[0] = writer.ToString();
            }
            using (StringWriter writer = new StringWriter())
            {
                dataTable.WriteXml(writer);
                values[1] = writer.ToString();
            }

            return values;
        }

        /// <summary>
        /// DataTable을 역직렬화한다.
        /// </summary>
        /// <param name="values"></param>
        /// <returns></returns>
        public static DataTable DeserializeDataTable(string[] values)
        {
            DataTable dataTable = new DataTable("dt");
                
            using (StringReader reader = new StringReader(values[0]))
            {
                dataTable.ReadXmlSchema(reader);
            }
            using (StringReader reader = new StringReader(values[1]))
            {
                dataTable.ReadXml(reader);
            }

            return dataTable;
        }
        #endregion

    }
}